package com.epam.ws.anatomist.validation;

import static org.springframework.ws.test.support.AssertionErrors.fail;
import net.javacrumbs.smock.common.MessageHelper;
import net.javacrumbs.smock.common.server.MockWebServiceClientResponseActions;

import org.custommonkey.xmlunit.DetailedDiff;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.DifferenceListener;
import org.custommonkey.xmlunit.XMLUnit;
import org.springframework.context.ApplicationContext;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.WebServiceMessageFactory;
import org.springframework.ws.context.DefaultMessageContext;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.soap.SoapVersion;
import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
import org.springframework.ws.test.server.ResponseActions;
import org.springframework.ws.test.support.MockStrategiesHelper;
import org.w3c.dom.Document;

import com.epam.ws.anatomist.common.exception.DifferenceFoundException;
import com.epam.ws.anatomist.common.exception.ErrorMessages;
import com.epam.ws.anatomist.log.Logger;

public class ResponseValidatorService
{

	private static DifferenceListener differenceListener = new DefaultDifferenceListener();

	private static final String VALIDATION_MESSAGE = "Validate actual response by control response with ResponseDifferenceListner";

	private static WebServiceMessageFactory messageFactory = createMessageFactory();

	private static MessageHelper messageHelper = new MessageHelper(messageFactory);

	static
	{
		XMLUnit.setIgnoreWhitespace(true);
	}

	/**
	 * Validate actual document by control document using {@link DifferenceListener}. Detailed log of differences will be logged in case of any difference found
	 * 
	 * @param control
	 * @param actual
	 */
	public static void validateDocumentByControlDocument(Document control, Document actual)
	{
		Logger.operation(VALIDATION_MESSAGE);
		boolean differenceFound = false;
		try
		{
			Diff myDiff = new Diff(control, actual);
			myDiff.overrideDifferenceListener(differenceListener);
			DetailedDiff detailedDiff = new DetailedDiff(myDiff);
			differenceFound = !(detailedDiff.similar());
		}
		catch (Exception e)
		{
			throw new DifferenceFoundException(ErrorMessages.VALIDATION_PROCESS_ERROR, e);
		}
		if (differenceFound)
		{
			throw new DifferenceFoundException(ErrorMessages.DIFFERENCE_FOUND_ERROR);
		}
	}

	public static ResponseActions validate(Object response, Object request)
	{
		DefaultMessageContext messageContext = new DefaultMessageContext(serialize(request), messageFactory);
		messageContext.setResponse(serialize(response));
		return validate(messageContext);
	}

	public static WebServiceMessage serialize(Object object)
	{
		try
		{
			WebServiceMessage message = messageHelper.serialize(object);
			if (message == null)
				fail("Can not serialize object " + object);
			return message;
		}
		catch (Exception e)
		{
			throw new IllegalArgumentException("Can not serialize object.", e);
		}
	}

	public static ResponseActions validate(MessageContext messageContext)
	{
		return new MockWebServiceClientResponseActions(messageContext);
	}

	/**
	* Creates a {@link WebServiceMessageFactory} using {@link MockStrategiesHelper}.
	* @param applicationContext
	* @return
	*/
	public static WebServiceMessageFactory createMessageFactory(ApplicationContext applicationContext)
	{
		if (applicationContext == null)
			return createMessageFactory();
		return new MockStrategiesHelper(applicationContext).getStrategy(WebServiceMessageFactory.class,
		        SaajSoapMessageFactory.class);
	}

	/**
	 * Creates SAAJ based message factory for SOAP 1.1.
	 * @return
	 */
	public static WebServiceMessageFactory createMessageFactory()
	{
		return createMessageFactory(SoapVersion.SOAP_11);
	}

	public static WebServiceMessageFactory createMessageFactory(SoapVersion soapVersion)
	{
		SaajSoapMessageFactory saajSoapMessageFactory = new SaajSoapMessageFactory();
		saajSoapMessageFactory.setSoapVersion(soapVersion);
		saajSoapMessageFactory.afterPropertiesSet();
		return saajSoapMessageFactory;
	}

}
